/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. *//* * JS execution context. */#include"jscntxtinlines.h"#include"mozilla/ArrayUtils.h"#include"mozilla/DebugOnly.h"#include"mozilla/MemoryReporting.h"#include"mozilla/Sprintf.h"#include<ctype.h>#include<stdarg.h>#include<string.h>#ifdef ANDROID# include <android/log.h># include <fstream># include <string>#endif // ANDROID#ifdef XP_WIN#include<processthreadsapi.h>#endif // XP_WIN#include"jsatom.h"#include"jscompartment.h"#include"jsdtoa.h"#include"jsexn.h"#include"jsfun.h"#include"jsgc.h"#include"jsiter.h"#include"jsnativestack.h"#include"jsobj.h"#include"jsopcode.h"#include"jsprf.h"#include"jspubtd.h"#include"jsscript.h"#include"jsstr.h"#include"jstypes.h"#include"jswatchpoint.h"#include"jswin.h"#include"gc/Marking.h"#include"jit/Ion.h"#include"jit/PcScriptCache.h"#include"js/CharacterEncoding.h"#include"vm/ErrorReporting.h"#include"vm/HelperThreads.h"#include"vm/Shape.h"#include"wasm/WasmSignalHandlers.h"#include"jsobjinlines.h"#include"jsscriptinlines.h"#include"vm/Stack-inl.h"usingnamespacejs;usingnamespacejs::gc;usingmozilla::DebugOnly;usingmozilla::PodArrayZero;usingmozilla::PointerRangeSize;booljs::AutoCycleDetector::init(){MOZ_ASSERT(cyclic);AutoCycleDetector::Vector&vector=cx->cycleDetectorVector();for(JSObject*obj2:vector){if(MOZ_UNLIKELY(obj==obj2))returntrue;}if(!vector.append(obj))returnfalse;cyclic=false;returntrue;}js::AutoCycleDetector::~AutoCycleDetector(){if(MOZ_LIKELY(!cyclic)){AutoCycleDetector::Vector&vec=cx->cycleDetectorVector();MOZ_ASSERT(vec.back()==obj);if(vec.length()>1){vec.popBack();}else{// Avoid holding on to unused heap allocations.vec.clearAndFree();}}}boolJSContext::init(ContextKindkind){// Skip most of the initialization if this thread will not be running JS.if(kind==ContextKind::Cooperative){// Get a platform-native handle for this thread, used by js::InterruptRunningJitCode.#ifdef XP_WINsize_topenFlags=THREAD_GET_CONTEXT|THREAD_SET_CONTEXT|THREAD_SUSPEND_RESUME|THREAD_QUERY_INFORMATION;HANDLEself=OpenThread(openFlags,false,GetCurrentThreadId());if(!self)returnfalse;static_assert(sizeof(HANDLE)<=sizeof(threadNative_),"need bigger field");threadNative_=(size_t)self;#elsestatic_assert(sizeof(pthread_t)<=sizeof(threadNative_),"need bigger field");threadNative_=(size_t)pthread_self();#endifif(!regexpStack.ref().init())returnfalse;if(!fx.initInstance())returnfalse;#ifdef JS_SIMULATORsimulator_=js::jit::Simulator::Create(this);if(!simulator_)returnfalse;#endifif(!wasm::EnsureSignalHandlers(this))returnfalse;}// Set the ContextKind last, so that ProtectedData checks will allow us to// initialize this context before it becomes the runtime's active context.kind_=kind;returntrue;}JSContext*js::NewContext(uint32_tmaxBytes,uint32_tmaxNurseryBytes,JSRuntime*parentRuntime){AutoNoteSingleThreadedRegionanstr;MOZ_RELEASE_ASSERT(!TlsContext.get());JSRuntime*runtime=js_new<JSRuntime>(parentRuntime);if(!runtime)returnnullptr;JSContext*cx=js_new<JSContext>(runtime,JS::ContextOptions());if(!cx){js_delete(runtime);returnnullptr;}if(!runtime->init(cx,maxBytes,maxNurseryBytes)){runtime->destroyRuntime();js_delete(cx);js_delete(runtime);returnnullptr;}if(!cx->init(ContextKind::Cooperative)){runtime->destroyRuntime();js_delete(cx);js_delete(runtime);returnnullptr;}returncx;}JSContext*js::NewCooperativeContext(JSContext*siblingContext){MOZ_RELEASE_ASSERT(!TlsContext.get());JSRuntime*runtime=siblingContext->runtime();JSContext*cx=js_new<JSContext>(runtime,JS::ContextOptions());if(!cx||!cx->init(ContextKind::Cooperative)){js_delete(cx);returnnullptr;}runtime->setNewbornActiveContext(cx);returncx;}voidjs::YieldCooperativeContext(JSContext*cx){MOZ_ASSERT(cx==TlsContext.get());MOZ_ASSERT(cx->runtime()->activeContext()==cx);cx->runtime()->setActiveContext(nullptr);}voidjs::ResumeCooperativeContext(JSContext*cx){MOZ_ASSERT(cx==TlsContext.get());MOZ_ASSERT(cx->runtime()->activeContext()==nullptr);cx->runtime()->setActiveContext(cx);}staticvoidFreeJobQueueHandling(JSContext*cx){if(!cx->jobQueue)return;cx->jobQueue->reset();FreeOp*fop=cx->defaultFreeOp();fop->delete_(cx->jobQueue.ref());cx->getIncumbentGlobalCallback=nullptr;cx->enqueuePromiseJobCallback=nullptr;cx->enqueuePromiseJobCallbackData=nullptr;}voidjs::DestroyContext(JSContext*cx){JS_AbortIfWrongThread(cx);if(cx->outstandingRequests!=0)MOZ_CRASH("Attempted to destroy a context while it is in a request.");cx->checkNoGCRooters();// Cancel all off thread Ion compiles before destroying a cooperative// context. Completed Ion compiles may try to interrupt arbitrary// cooperative contexts which they have read off the owner context of a// zone group. See HelperThread::handleIonWorkload.CancelOffThreadIonCompile(cx->runtime());FreeJobQueueHandling(cx);if(cx->runtime()->cooperatingContexts().length()==1){// Destroy the runtime along with its last context.cx->runtime()->destroyRuntime();js_delete(cx->runtime());js_delete_poison(cx);}else{DebugOnly<bool>found=false;for(size_ti=0;i<cx->runtime()->cooperatingContexts().length();i++){CooperatingContext&target=cx->runtime()->cooperatingContexts()[i];if(cx==target.context()){cx->runtime()->cooperatingContexts().erase(&target);found=true;break;}}MOZ_ASSERT(found);cx->runtime()->deleteActiveContext(cx);}}voidJS::RootingContext::checkNoGCRooters(){#ifdef DEBUGfor(autoconst&stackRootPtr:stackRoots_)MOZ_ASSERT(stackRootPtr==nullptr);#endif}boolAutoResolving::alreadyStartedSlow()const{MOZ_ASSERT(link);AutoResolving*cursor=link;do{MOZ_ASSERT(this!=cursor);if(object.get()==cursor->object&&id.get()==cursor->id&&kind==cursor->kind)returntrue;}while(!!(cursor=cursor->link));returnfalse;}staticvoidReportError(JSContext*cx,JSErrorReport*reportp,JSErrorCallbackcallback,void*userRef){/* * Check the error report, and set a JavaScript-catchable exception * if the error is defined to have an associated exception. If an * exception is thrown, then the JSREPORT_EXCEPTION flag will be set * on the error report, and exception-aware hosts should ignore it. */MOZ_ASSERT(reportp);if((!callback||callback==GetErrorMessage)&&reportp->errorNumber==JSMSG_UNCAUGHT_EXCEPTION){reportp->flags|=JSREPORT_EXCEPTION;}if(JSREPORT_IS_WARNING(reportp->flags)){CallWarningReporter(cx,reportp);return;}ErrorToException(cx,reportp,callback,userRef);}/* * The given JSErrorReport object have been zeroed and must not outlive * cx->fp() (otherwise owned fields may become invalid). */staticvoidPopulateReportBlame(JSContext*cx,JSErrorReport*report){JSCompartment*compartment=cx->compartment();if(!compartment)return;/* * Walk stack until we find a frame that is associated with a non-builtin * rather than a builtin frame and which we're allowed to know about. */NonBuiltinFrameIteriter(cx,compartment->principals());if(iter.done())return;report->filename=iter.filename();report->lineno=iter.computeLine(&report->column);// XXX: Make the column 1-based as in other browsers, instead of 0-based// which is how SpiderMonkey stores it internally. This will be// unnecessary once bug 1144340 is fixed.report->column++;report->isMuted=iter.mutedErrors();}/* * Since memory has been exhausted, avoid the normal error-handling path which * allocates an error object, report and callstack. If code is running, simply * throw the static atom "out of memory". If code is not running, call the * error reporter directly. * * Furthermore, callers of ReportOutOfMemory (viz., malloc) assume a GC does * not occur, so GC must be avoided or suppressed. */voidjs::ReportOutOfMemory(JSContext*cx){#ifdef JS_MORE_DETERMINISTIC/* * OOMs are non-deterministic, especially across different execution modes * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr * so that the fuzzers can detect this. */fprintf(stderr,"ReportOutOfMemory called\n");#endifif(cx->helperThread())returncx->addPendingOutOfMemory();cx->runtime()->hadOutOfMemory=true;AutoSuppressGCsuppressGC(cx);/* Report the oom. */if(JS::OutOfMemoryCallbackoomCallback=cx->runtime()->oomCallback)oomCallback(cx,cx->runtime()->oomCallbackData);cx->setPendingException(StringValue(cx->names().outOfMemory));}mozilla::GenericErrorResult<OOM&>js::ReportOutOfMemoryResult(JSContext*cx){ReportOutOfMemory(cx);returncx->alreadyReportedOOM();}voidjs::ReportOverRecursed(JSContext*maybecx,unsignederrorNumber){#ifdef JS_MORE_DETERMINISTIC/* * We cannot make stack depth deterministic across different * implementations (e.g. JIT vs. interpreter will differ in * their maximum stack depth). * However, we can detect externally when we hit the maximum * stack depth which is useful for external testing programs * like fuzzers. */fprintf(stderr,"ReportOverRecursed called\n");#endifif(maybecx){if(!maybecx->helperThread()){JS_ReportErrorNumberASCII(maybecx,GetErrorMessage,nullptr,errorNumber);maybecx->overRecursed_=true;}else{maybecx->addPendingOverRecursed();}}}JS_FRIEND_API(void)js::ReportOverRecursed(JSContext*maybecx){ReportOverRecursed(maybecx,JSMSG_OVER_RECURSED);}voidjs::ReportAllocationOverflow(JSContext*cx){if(!cx)return;if(cx->helperThread())return;AutoSuppressGCsuppressGC(cx);JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_ALLOC_OVERFLOW);}/* * Given flags and the state of cx, decide whether we should report an * error, a warning, or just continue execution normally. Return * true if we should continue normally, without reporting anything; * otherwise, adjust *flags as appropriate and return false. */staticboolcheckReportFlags(JSContext*cx,unsigned*flags){if(JSREPORT_IS_STRICT(*flags)){/* Warning/error only when JSOPTION_STRICT is set. */if(!cx->compartment()->behaviors().extraWarnings(cx))returntrue;}/* Warnings become errors when JSOPTION_WERROR is set. */if(JSREPORT_IS_WARNING(*flags)&&cx->options().werror())*flags&=~JSREPORT_WARNING;returnfalse;}booljs::ReportErrorVA(JSContext*cx,unsignedflags,constchar*format,ErrorArgumentsTypeargumentsType,va_listap){JSErrorReportreport;if(checkReportFlags(cx,&flags))returntrue;UniqueCharsmessage(JS_vsmprintf(format,ap));if(!message){ReportOutOfMemory(cx);returnfalse;}MOZ_ASSERT_IF(argumentsType==ArgumentsAreASCII,JS::StringIsASCII(message.get()));report.flags=flags;report.errorNumber=JSMSG_USER_DEFINED_ERROR;if(argumentsType==ArgumentsAreASCII||argumentsType==ArgumentsAreUTF8){report.initOwnedMessage(message.release());}else{MOZ_ASSERT(argumentsType==ArgumentsAreLatin1);Latin1Charslatin1(message.get(),strlen(message.get()));UTF8CharsZutf8(JS::CharsToNewUTF8CharsZ(cx,latin1));if(!utf8)returnfalse;report.initOwnedMessage(reinterpret_cast<constchar*>(utf8.get()));}PopulateReportBlame(cx,&report);boolwarning=JSREPORT_IS_WARNING(report.flags);ReportError(cx,&report,nullptr,nullptr);returnwarning;}/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */voidjs::ReportUsageErrorASCII(JSContext*cx,HandleObjectcallee,constchar*msg){constchar*usageStr="usage";PropertyName*usageAtom=Atomize(cx,usageStr,strlen(usageStr))->asPropertyName();RootedIdid(cx,NameToId(usageAtom));DebugOnly<Shape*>shape=static_cast<Shape*>(callee->as<JSFunction>().lookup(cx,id));MOZ_ASSERT(!shape->configurable());MOZ_ASSERT(!shape->writable());MOZ_ASSERT(shape->hasDefaultGetter());RootedValueusage(cx);if(!JS_GetProperty(cx,callee,"usage",&usage))return;if(!usage.isString()){JS_ReportErrorASCII(cx,"%s",msg);}else{RootedStringusageStr(cx,usage.toString());JSAutoByteStringstr;if(!str.encodeUtf8(cx,usageStr))return;JS_ReportErrorUTF8(cx,"%s. Usage: %s",msg,str.ptr());}}enumclassPrintErrorKind{Error,Warning,StrictWarning,Note};staticvoidPrintErrorLine(JSContext*cx,FILE*file,constchar*prefix,JSErrorReport*report){if(constchar16_t*linebuf=report->linebuf()){size_tn=report->linebufLength();fputs(":\n",file);if(prefix)fputs(prefix,file);for(size_ti=0;i<n;i++)fputc(static_cast<char>(linebuf[i]),file);// linebuf usually ends with a newline. If not, add one here.if(n==0||linebuf[n-1]!='\n')fputc('\n',file);if(prefix)fputs(prefix,file);n=report->tokenOffset();for(size_ti=0,j=0;i<n;i++){if(linebuf[i]=='\t'){for(size_tk=(j+8)&~7;j<k;j++)fputc('.',file);continue;}fputc('.',file);j++;}fputc('^',file);}}staticvoidPrintErrorLine(JSContext*cx,FILE*file,constchar*prefix,JSErrorNotes::Note*note){}template<typenameT>staticboolPrintSingleError(JSContext*cx,FILE*file,JS::ConstUTF8CharsZtoStringResult,T*report,PrintErrorKindkind){UniqueCharsprefix;if(report->filename)prefix=JS_smprintf("%s:",report->filename);if(report->lineno){prefix=JS_smprintf("%s%u:%u ",prefix?prefix.get():"",report->lineno,report->column);}if(kind!=PrintErrorKind::Error){constchar*kindPrefix=nullptr;switch(kind){casePrintErrorKind::Error:MOZ_CRASH("unreachable");casePrintErrorKind::Warning:kindPrefix="warning";break;casePrintErrorKind::StrictWarning:kindPrefix="strict warning";break;casePrintErrorKind::Note:kindPrefix="note";break;}prefix=JS_smprintf("%s%s: ",prefix?prefix.get():"",kindPrefix);}constchar*message=toStringResult?toStringResult.c_str():report->message().c_str();/* embedded newlines -- argh! */constchar*ctmp;while((ctmp=strchr(message,'\n'))!=0){ctmp++;if(prefix)fputs(prefix.get(),file);fwrite(message,1,ctmp-message,file);message=ctmp;}/* If there were no filename or lineno, the prefix might be empty */if(prefix)fputs(prefix.get(),file);fputs(message,file);PrintErrorLine(cx,file,prefix.get(),report);fputc('\n',file);fflush(file);returntrue;}booljs::PrintError(JSContext*cx,FILE*file,JS::ConstUTF8CharsZtoStringResult,JSErrorReport*report,boolreportWarnings){MOZ_ASSERT(report);/* Conditionally ignore reported warnings. */if(JSREPORT_IS_WARNING(report->flags)&&!reportWarnings)returnfalse;PrintErrorKindkind=PrintErrorKind::Error;if(JSREPORT_IS_WARNING(report->flags)){if(JSREPORT_IS_STRICT(report->flags))kind=PrintErrorKind::StrictWarning;elsekind=PrintErrorKind::Warning;}PrintSingleError(cx,file,toStringResult,report,kind);if(report->notes){for(auto&¬e:*report->notes)PrintSingleError(cx,file,JS::ConstUTF8CharsZ(),note.get(),PrintErrorKind::Note);}returntrue;}classMOZ_RAIIAutoMessageArgs{size_ttotalLength_;/* only {0} thru {9} supported */mozilla::Array<constchar*,JS::MaxNumErrorArguments>args_;mozilla::Array<size_t,JS::MaxNumErrorArguments>lengths_;uint16_tcount_;boolallocatedElements_:1;public:AutoMessageArgs():totalLength_(0),count_(0),allocatedElements_(false){PodArrayZero(args_);}~AutoMessageArgs(){/* free the arguments only if we allocated them */if(allocatedElements_){uint16_ti=0;while(i<count_){if(args_[i])js_free((void*)args_[i]);i++;}}}constchar*args(size_ti)const{MOZ_ASSERT(i<count_);returnargs_[i];}size_ttotalLength()const{returntotalLength_;}size_tlengths(size_ti)const{MOZ_ASSERT(i<count_);returnlengths_[i];}uint16_tcount()const{returncount_;}/* Gather the arguments into an array, and accumulate their sizes. */boolinit(JSContext*cx,constchar16_t**argsArg,uint16_tcountArg,ErrorArgumentsTypetypeArg,va_listap){MOZ_ASSERT(countArg>0);count_=countArg;for(uint16_ti=0;i<count_;i++){switch(typeArg){caseArgumentsAreASCII:caseArgumentsAreUTF8:{MOZ_ASSERT(!argsArg);args_[i]=va_arg(ap,char*);MOZ_ASSERT_IF(typeArg==ArgumentsAreASCII,JS::StringIsASCII(args_[i]));lengths_[i]=strlen(args_[i]);break;}caseArgumentsAreLatin1:{MOZ_ASSERT(!argsArg);constLatin1Char*latin1=va_arg(ap,Latin1Char*);size_tlen=strlen(reinterpret_cast<constchar*>(latin1));mozilla::Range<constLatin1Char>range(latin1,len);char*utf8=JS::CharsToNewUTF8CharsZ(cx,range).c_str();if(!utf8)returnfalse;args_[i]=utf8;lengths_[i]=strlen(utf8);allocatedElements_=true;break;}caseArgumentsAreUnicode:{constchar16_t*uc=argsArg?argsArg[i]:va_arg(ap,char16_t*);size_tlen=js_strlen(uc);mozilla::Range<constchar16_t>range(uc,len);char*utf8=JS::CharsToNewUTF8CharsZ(cx,range).c_str();if(!utf8)returnfalse;args_[i]=utf8;lengths_[i]=strlen(utf8);allocatedElements_=true;break;}}totalLength_+=lengths_[i];}returntrue;}};staticvoidSetExnType(JSErrorReport*reportp,int16_texnType){reportp->exnType=exnType;}staticvoidSetExnType(JSErrorNotes::Note*notep,int16_texnType){// Do nothing for JSErrorNotes::Note.}/* * The arguments from ap need to be packaged up into an array and stored * into the report struct. * * The format string addressed by the error number may contain operands * identified by the format {N}, where N is a decimal digit. Each of these * is to be replaced by the Nth argument from the va_list. The complete * message is placed into reportp->message_. * * Returns true if the expansion succeeds (can fail if out of memory). */template<typenameT>boolExpandErrorArgumentsHelper(JSContext*cx,JSErrorCallbackcallback,void*userRef,constunsignederrorNumber,constchar16_t**messageArgs,ErrorArgumentsTypeargumentsType,T*reportp,va_listap){constJSErrorFormatString*efs;if(!callback)callback=GetErrorMessage;{AutoSuppressGCsuppressGC(cx);efs=callback(userRef,errorNumber);}if(efs){SetExnType(reportp,efs->exnType);MOZ_ASSERT_IF(argumentsType==ArgumentsAreASCII,JS::StringIsASCII(efs->format));uint16_targCount=efs->argCount;MOZ_RELEASE_ASSERT(argCount<=JS::MaxNumErrorArguments);if(argCount>0){/* * Parse the error format, substituting the argument X * for {X} in the format. */if(efs->format){constchar*fmt;char*out;#ifdef DEBUGintexpandedArgs=0;#endifsize_texpandedLength;size_tlen=strlen(efs->format);AutoMessageArgsargs;if(!args.init(cx,messageArgs,argCount,argumentsType,ap))returnfalse;expandedLength=len-(3*args.count())/* exclude the {n} */+args.totalLength();/* * Note - the above calculation assumes that each argument * is used once and only once in the expansion !!! */char*utf8=out=cx->pod_malloc<char>(expandedLength+1);if(!out)returnfalse;fmt=efs->format;while(*fmt){if(*fmt=='{'){if(isdigit(fmt[1])){intd=JS7_UNDEC(fmt[1]);MOZ_RELEASE_ASSERT(d<args.count());strncpy(out,args.args(d),args.lengths(d));out+=args.lengths(d);fmt+=3;#ifdef DEBUGexpandedArgs++;#endifcontinue;}}*out++=*fmt++;}MOZ_ASSERT(expandedArgs==args.count());*out=0;reportp->initOwnedMessage(utf8);}}else{/* Non-null messageArgs should have at least one non-null arg. */MOZ_ASSERT(!messageArgs);/* * Zero arguments: the format string (if it exists) is the * entire message. */if(efs->format)reportp->initBorrowedMessage(efs->format);}}if(!reportp->message()){/* where's the right place for this ??? */constchar*defaultErrorMessage="No error message available for error number %d";size_tnbytes=strlen(defaultErrorMessage)+16;char*message=cx->pod_malloc<char>(nbytes);if(!message)returnfalse;snprintf(message,nbytes,defaultErrorMessage,errorNumber);reportp->initOwnedMessage(message);}returntrue;}booljs::ExpandErrorArgumentsVA(JSContext*cx,JSErrorCallbackcallback,void*userRef,constunsignederrorNumber,constchar16_t**messageArgs,ErrorArgumentsTypeargumentsType,JSErrorReport*reportp,va_listap){returnExpandErrorArgumentsHelper(cx,callback,userRef,errorNumber,messageArgs,argumentsType,reportp,ap);}booljs::ExpandErrorArgumentsVA(JSContext*cx,JSErrorCallbackcallback,void*userRef,constunsignederrorNumber,constchar16_t**messageArgs,ErrorArgumentsTypeargumentsType,JSErrorNotes::Note*notep,va_listap){returnExpandErrorArgumentsHelper(cx,callback,userRef,errorNumber,messageArgs,argumentsType,notep,ap);}booljs::ReportErrorNumberVA(JSContext*cx,unsignedflags,JSErrorCallbackcallback,void*userRef,constunsignederrorNumber,ErrorArgumentsTypeargumentsType,va_listap){JSErrorReportreport;boolwarning;if(checkReportFlags(cx,&flags))returntrue;warning=JSREPORT_IS_WARNING(flags);report.flags=flags;report.errorNumber=errorNumber;PopulateReportBlame(cx,&report);if(!ExpandErrorArgumentsVA(cx,callback,userRef,errorNumber,nullptr,argumentsType,&report,ap)){returnfalse;}ReportError(cx,&report,callback,userRef);returnwarning;}staticboolExpandErrorArguments(JSContext*cx,JSErrorCallbackcallback,void*userRef,constunsignederrorNumber,constchar16_t**messageArgs,ErrorArgumentsTypeargumentsType,JSErrorReport*reportp,...){va_listap;va_start(ap,reportp);boolexpanded=js::ExpandErrorArgumentsVA(cx,callback,userRef,errorNumber,messageArgs,argumentsType,reportp,ap);va_end(ap);returnexpanded;}booljs::ReportErrorNumberUCArray(JSContext*cx,unsignedflags,JSErrorCallbackcallback,void*userRef,constunsignederrorNumber,constchar16_t**args){if(checkReportFlags(cx,&flags))returntrue;boolwarning=JSREPORT_IS_WARNING(flags);JSErrorReportreport;report.flags=flags;report.errorNumber=errorNumber;PopulateReportBlame(cx,&report);if(!ExpandErrorArguments(cx,callback,userRef,errorNumber,args,ArgumentsAreUnicode,&report)){returnfalse;}ReportError(cx,&report,callback,userRef);returnwarning;}booljs::ReportIsNotDefined(JSContext*cx,HandleIdid){JSAutoByteStringprintable;if(ValueToPrintable(cx,IdToValue(id),&printable)){JS_ReportErrorNumberLatin1(cx,GetErrorMessage,nullptr,JSMSG_NOT_DEFINED,printable.ptr());}returnfalse;}booljs::ReportIsNotDefined(JSContext*cx,HandlePropertyNamename){RootedIdid(cx,NameToId(name));returnReportIsNotDefined(cx,id);}booljs::ReportIsNullOrUndefined(JSContext*cx,intspindex,HandleValuev,HandleStringfallback){boolok;UniqueCharsbytes=DecompileValueGenerator(cx,spindex,v,fallback);if(!bytes)returnfalse;if(strcmp(bytes.get(),js_undefined_str)==0||strcmp(bytes.get(),js_null_str)==0){ok=JS_ReportErrorFlagsAndNumberLatin1(cx,JSREPORT_ERROR,GetErrorMessage,nullptr,JSMSG_NO_PROPERTIES,bytes.get());}elseif(v.isUndefined()){ok=JS_ReportErrorFlagsAndNumberLatin1(cx,JSREPORT_ERROR,GetErrorMessage,nullptr,JSMSG_UNEXPECTED_TYPE,bytes.get(),js_undefined_str);}else{MOZ_ASSERT(v.isNull());ok=JS_ReportErrorFlagsAndNumberLatin1(cx,JSREPORT_ERROR,GetErrorMessage,nullptr,JSMSG_UNEXPECTED_TYPE,bytes.get(),js_null_str);}returnok;}voidjs::ReportMissingArg(JSContext*cx,HandleValuev,unsignedarg){charargbuf[11];UniqueCharsbytes;SprintfLiteral(argbuf,"%u",arg);if(IsFunctionObject(v)){RootedAtomname(cx,v.toObject().as<JSFunction>().explicitName());bytes=DecompileValueGenerator(cx,JSDVG_SEARCH_STACK,v,name);if(!bytes)return;}JS_ReportErrorNumberLatin1(cx,GetErrorMessage,nullptr,JSMSG_MISSING_FUN_ARG,argbuf,bytes?bytes.get():"");}booljs::ReportValueErrorFlags(JSContext*cx,unsignedflags,constunsignederrorNumber,intspindex,HandleValuev,HandleStringfallback,constchar*arg1,constchar*arg2){UniqueCharsbytes;boolok;MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount>=1);MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount<=3);bytes=DecompileValueGenerator(cx,spindex,v,fallback);if(!bytes)returnfalse;ok=JS_ReportErrorFlagsAndNumberLatin1(cx,flags,GetErrorMessage,nullptr,errorNumber,bytes.get(),arg1,arg2);returnok;}JSObject*js::CreateErrorNotesArray(JSContext*cx,JSErrorReport*report){RootedArrayObjectnotesArray(cx,NewDenseEmptyArray(cx));if(!notesArray)returnnullptr;if(!report->notes)returnnotesArray;for(auto&¬e:*report->notes){RootedPlainObjectnoteObj(cx,NewBuiltinClassInstance<PlainObject>(cx));if(!noteObj)returnnullptr;RootedStringmessageStr(cx,note->newMessageString(cx));if(!messageStr)returnnullptr;RootedValuemessageVal(cx,StringValue(messageStr));if(!DefineProperty(cx,noteObj,cx->names().message,messageVal))returnnullptr;RootedValuefilenameVal(cx);if(note->filename){RootedStringfilenameStr(cx,NewStringCopyZ<CanGC>(cx,note->filename));if(!filenameStr)returnnullptr;filenameVal=StringValue(filenameStr);}if(!DefineProperty(cx,noteObj,cx->names().fileName,filenameVal))returnnullptr;RootedValuelinenoVal(cx,Int32Value(note->lineno));if(!DefineProperty(cx,noteObj,cx->names().lineNumber,linenoVal))returnnullptr;RootedValuecolumnVal(cx,Int32Value(note->column));if(!DefineProperty(cx,noteObj,cx->names().columnNumber,columnVal))returnnullptr;if(!NewbornArrayPush(cx,notesArray,ObjectValue(*noteObj)))returnnullptr;}returnnotesArray;}constJSErrorFormatStringjs_ErrorFormatString[JSErr_Limit]={#define MSG_DEF(name, count, exception, format) \ { #name, format, count, exception } ,#include"js.msg"#undef MSG_DEF};JS_FRIEND_API(constJSErrorFormatString*)js::GetErrorMessage(void*userRef,constunsignederrorNumber){if(errorNumber>0&&errorNumber<JSErr_Limit)return&js_ErrorFormatString[errorNumber];returnnullptr;}voidJSContext::recoverFromOutOfMemory(){if(helperThread()){// Keep in sync with addPendingOutOfMemory.if(ParseTask*task=helperThread()->parseTask())task->outOfMemory=false;}else{if(isExceptionPending()){MOZ_ASSERT(isThrowingOutOfMemory());clearPendingException();}}}staticboolInternalEnqueuePromiseJobCallback(JSContext*cx,JS::HandleObjectjob,JS::HandleObjectallocationSite,JS::HandleObjectincumbentGlobal,void*data){MOZ_ASSERT(job);returncx->jobQueue->append(job);}staticboolInternalStartAsyncTaskCallback(JSContext*cx,JS::AsyncTask*task){task->user=cx;ExclusiveData<InternalAsyncTasks>::GuardasyncTasks=cx->asyncTasks.lock();asyncTasks->outstanding++;returntrue;}staticboolInternalFinishAsyncTaskCallback(JS::AsyncTask*task){JSContext*cx=(JSContext*)task->user;ExclusiveData<InternalAsyncTasks>::GuardasyncTasks=cx->asyncTasks.lock();MOZ_ASSERT(asyncTasks->outstanding>0);asyncTasks->outstanding--;returnasyncTasks->finished.append(task);}namespace{classMOZ_STACK_CLASSReportExceptionClosure:publicScriptEnvironmentPreparer::Closure{public:explicitReportExceptionClosure(HandleValueexn):exn_(exn){}booloperator()(JSContext*cx)override{cx->setPendingException(exn_);returnfalse;}private:HandleValueexn_;};}// anonymous namespaceJS_FRIEND_API(bool)js::UseInternalJobQueues(JSContext*cx){// Internal job queue handling must be set up very early. Self-hosting// initialization is as good a marker for that as any.MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(),"js::UseInternalJobQueues must be called early during runtime startup.");MOZ_ASSERT(!cx->jobQueue);auto*queue=cx->new_<PersistentRooted<JobQueue>>(cx,JobQueue(SystemAllocPolicy()));if(!queue)returnfalse;cx->jobQueue=queue;JS::SetEnqueuePromiseJobCallback(cx,InternalEnqueuePromiseJobCallback);JS::SetAsyncTaskCallbacks(cx,InternalStartAsyncTaskCallback,InternalFinishAsyncTaskCallback);returntrue;}JS_FRIEND_API(void)js::StopDrainingJobQueue(JSContext*cx){MOZ_ASSERT(cx->jobQueue);cx->stopDrainingJobQueue=true;}JS_FRIEND_API(void)js::RunJobs(JSContext*cx){MOZ_ASSERT(cx->jobQueue);if(cx->drainingJobQueue||cx->stopDrainingJobQueue)return;while(true){// Wait for any outstanding async tasks to finish so that the// finishedAsyncTasks list is fixed.while(true){AutoLockHelperThreadStatelock;if(!cx->asyncTasks.lock()->outstanding)break;HelperThreadState().wait(lock,GlobalHelperThreadState::CONSUMER);}// Lock the whole time while copying back the asyncTasks finished queue// so that any new tasks created during finish() cannot racily join the// job queue. Call finish() only thereafter, to avoid a circular mutex// dependency (see also bug 1297901).Vector<JS::AsyncTask*,0,SystemAllocPolicy>finished;{ExclusiveData<InternalAsyncTasks>::GuardasyncTasks=cx->asyncTasks.lock();finished=Move(asyncTasks->finished);asyncTasks->finished.clear();}for(JS::AsyncTask*task:finished)task->finish(cx);// It doesn't make sense for job queue draining to be reentrant. At the// same time we don't want to assert against it, because that'd make// drainJobQueue unsafe for fuzzers. We do want fuzzers to test this,// so we simply ignore nested calls of drainJobQueue.cx->drainingJobQueue=true;RootedObjectjob(cx);JS::HandleValueArrayargs(JS::HandleValueArray::empty());RootedValuerval(cx);// Execute jobs in a loop until we've reached the end of the queue.// Since executing a job can trigger enqueuing of additional jobs,// it's crucial to re-check the queue length during each iteration.for(size_ti=0;i<cx->jobQueue->length();i++){// A previous job might have set this flag. E.g., the js shell// sets it if the `quit` builtin function is called.if(cx->stopDrainingJobQueue)break;job=cx->jobQueue->get()[i];// It's possible that queue draining was interrupted prematurely,// leaving the queue partly processed. In that case, slots for// already-executed entries will contain nullptrs, which we should// just skip.if(!job)continue;cx->jobQueue->get()[i]=nullptr;AutoCompartmentac(cx,job);{if(!JS::Call(cx,UndefinedHandleValue,job,args,&rval)){// Nothing we can do about uncatchable exceptions.if(!cx->isExceptionPending())continue;RootedValueexn(cx);if(cx->getPendingException(&exn)){/* * Clear the exception, because * PrepareScriptEnvironmentAndInvoke will assert that we don't * have one. */cx->clearPendingException();ReportExceptionClosurereportExn(exn);PrepareScriptEnvironmentAndInvoke(cx,cx->global(),reportExn);}}}}cx->drainingJobQueue=false;if(cx->stopDrainingJobQueue){cx->stopDrainingJobQueue=false;break;}cx->jobQueue->clear();// It's possible a job added an async task, and it's also possible// that task has already finished.{ExclusiveData<InternalAsyncTasks>::GuardasyncTasks=cx->asyncTasks.lock();if(asyncTasks->outstanding==0&&asyncTasks->finished.length()==0)break;}}}JS::ErrorJSContext::reportedError;JS::OOMJSContext::reportedOOM;mozilla::GenericErrorResult<OOM&>JSContext::alreadyReportedOOM(){#ifdef DEBUGif(helperThread()){// Keep in sync with addPendingOutOfMemory.if(ParseTask*task=helperThread()->parseTask())MOZ_ASSERT(task->outOfMemory);}else{MOZ_ASSERT(isThrowingOutOfMemory());}#endifreturnmozilla::Err(reportedOOM);}mozilla::GenericErrorResult<JS::Error&>JSContext::alreadyReportedError(){#ifdef DEBUGif(!helperThread())MOZ_ASSERT(isExceptionPending());#endifreturnmozilla::Err(reportedError);}JSContext::JSContext(JSRuntime*runtime,constJS::ContextOptions&options):runtime_(runtime),kind_(ContextKind::Background),threadNative_(0),helperThread_(nullptr),options_(options),arenas_(nullptr),enterCompartmentDepth_(0),jitActivation(nullptr),activation_(nullptr),profilingActivation_(nullptr),nativeStackBase(GetNativeStackBase()),entryMonitor(nullptr),noExecuteDebuggerTop(nullptr),handlingSegFault(false),activityCallback(nullptr),activityCallbackArg(nullptr),requestDepth(0),#ifdef DEBUGcheckRequestDepth(0),#endif#ifdef JS_SIMULATORsimulator_(nullptr),#endif#ifdef JS_TRACE_LOGGINGtraceLogger(nullptr),#endifautoFlushICache_(nullptr),dtoaState(nullptr),heapState(JS::HeapState::Idle),suppressGC(0),#ifdef DEBUGionCompiling(false),ionCompilingSafeForMinorGC(false),performingGC(false),gcSweeping(false),gcHelperStateThread(false),noGCOrAllocationCheck(0),noNurseryAllocationCheck(0),disableStrictProxyCheckingCount(0),#endif#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)runningOOMTest(false),#endifenableAccessValidation(false),inUnsafeRegion(0),generationalDisabled(0),compactingDisabledCount(0),keepAtoms(0),suppressProfilerSampling(false),tempLifoAlloc_((size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),debuggerMutations(0),propertyRemovals(0),ionPcScriptCache(nullptr),throwing(false),overRecursed_(false),propagatingForcedReturn_(false),liveVolatileJitFrameIterators_(nullptr),reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),resolvingList(nullptr),#ifdef DEBUGenteredPolicy(nullptr),#endifgeneratingError(false),cycleDetectorVector_(this),data(nullptr),outstandingRequests(0),jitIsBroken(false),asyncCauseForNewActivations(nullptr),asyncCallIsExplicit(false),interruptCallbackDisabled(false),interrupt_(false),handlingJitInterrupt_(false),osrTempData_(nullptr),ionReturnOverride_(MagicValue(JS_ARG_POISON)),jitStackLimit(UINTPTR_MAX),jitStackLimitNoInterrupt(UINTPTR_MAX),getIncumbentGlobalCallback(nullptr),enqueuePromiseJobCallback(nullptr),enqueuePromiseJobCallbackData(nullptr),jobQueue(nullptr),drainingJobQueue(false),stopDrainingJobQueue(false),asyncTasks(mutexid::InternalAsyncTasks),promiseRejectionTrackerCallback(nullptr),promiseRejectionTrackerCallbackData(nullptr){MOZ_ASSERT(static_cast<JS::RootingContext*>(this)==JS::RootingContext::get(this));MOZ_ASSERT(!TlsContext.get());TlsContext.set(this);for(size_ti=0;i<mozilla::ArrayLength(nativeStackQuota);i++)nativeStackQuota[i]=0;}JSContext::~JSContext(){// Clear the ContextKind first, so that ProtectedData checks will allow us to// destroy this context even if the runtime is already gone.kind_=ContextKind::Background;#ifdef XP_WINif(threadNative_)CloseHandle((HANDLE)threadNative_.ref());#endif/* Free the stuff hanging off of cx. */MOZ_ASSERT(!resolvingList);js_delete(ionPcScriptCache.ref());if(dtoaState)DestroyDtoaState(dtoaState);fx.destroyInstance();freeOsrTempData();#ifdef JS_SIMULATORjs::jit::Simulator::Destroy(simulator_);#endif#ifdef JS_TRACE_LOGGINGif(traceLogger)DestroyTraceLogger(traceLogger);#endifMOZ_ASSERT(TlsContext.get()==this);TlsContext.set(nullptr);}voidJSContext::setRuntime(JSRuntime*rt){MOZ_ASSERT(!resolvingList);MOZ_ASSERT(!compartment());MOZ_ASSERT(!activation());MOZ_ASSERT(!unwrappedException_.ref().initialized());MOZ_ASSERT(!asyncStackForNewActivations_.ref().initialized());runtime_=rt;}boolJSContext::getPendingException(MutableHandleValuerval){MOZ_ASSERT(throwing);rval.set(unwrappedException());if(IsAtomsCompartment(compartment()))returntrue;boolwasOverRecursed=overRecursed_;clearPendingException();if(!compartment()->wrap(this,rval))returnfalse;assertSameCompartment(this,rval);setPendingException(rval);overRecursed_=wasOverRecursed;returntrue;}boolJSContext::isThrowingOutOfMemory(){returnthrowing&&unwrappedException()==StringValue(names().outOfMemory);}boolJSContext::isClosingGenerator(){returnthrowing&&unwrappedException().isMagic(JS_GENERATOR_CLOSING);}boolJSContext::isThrowingDebuggeeWouldRun(){returnthrowing&&unwrappedException().isObject()&&unwrappedException().toObject().is<ErrorObject>()&&unwrappedException().toObject().as<ErrorObject>().type()==JSEXN_DEBUGGEEWOULDRUN;}staticboolComputeIsJITBroken(){#if !defined(ANDROID)returnfalse;#else // ANDROIDif(getenv("JS_IGNORE_JIT_BROKENNESS")){returnfalse;}std::stringline;// Check for the known-bad kernel version (2.6.29).std::ifstreamosrelease("/proc/sys/kernel/osrelease");std::getline(osrelease,line);__android_log_print(ANDROID_LOG_INFO,"Gecko","Detected osrelease `%s'",line.c_str());if(line.npos==line.find("2.6.29")){// We're using something other than 2.6.29, so the JITs should work.__android_log_print(ANDROID_LOG_INFO,"Gecko","JITs are not broken");returnfalse;}// We're using 2.6.29, and this causes trouble with the JITs on i9000.line="";boolbroken=false;std::ifstreamcpuinfo("/proc/cpuinfo");do{if(0==line.find("Hardware")){staticconstchar*constblacklist[]={"SCH-I400",// Samsung Continuum"SGH-T959",// Samsung i9000, Vibrant device"SGH-I897",// Samsung i9000, Captivate device"SCH-I500",// Samsung i9000, Fascinate device"SPH-D700",// Samsung i9000, Epic device"GT-I9000",// Samsung i9000, UK/Europe devicenullptr};for(constchar*const*hw=&blacklist[0];*hw;++hw){if(line.npos!=line.find(*hw)){__android_log_print(ANDROID_LOG_INFO,"Gecko","Blacklisted device `%s'",*hw);broken=true;break;}}break;}std::getline(cpuinfo,line);}while(!cpuinfo.fail()&&!cpuinfo.eof());__android_log_print(ANDROID_LOG_INFO,"Gecko","JITs are %sbroken",broken?"":"not ");returnbroken;#endif // ifndef ANDROID}staticboolIsJITBrokenHere(){staticboolcomputedIsBroken=false;staticboolisBroken=false;if(!computedIsBroken){isBroken=ComputeIsJITBroken();computedIsBroken=true;}returnisBroken;}voidJSContext::updateJITEnabled(){jitIsBroken=IsJITBrokenHere();}size_tJSContext::sizeOfExcludingThis(mozilla::MallocSizeOfmallocSizeOf)const{/* * There are other JSContext members that could be measured; the following * ones have been found by DMD to be worth measuring. More stuff may be * added later. */returncycleDetectorVector().sizeOfExcludingThis(mallocSizeOf);}voidJSContext::trace(JSTracer*trc){cycleDetectorVector().trace(trc);if(trc->isMarkingTracer()&&compartment_)compartment_->mark();}void*JSContext::stackLimitAddressForJitCode(JS::StackKindkind){#ifdef JS_SIMULATORreturnaddressOfSimulatorStackLimit();#elsereturnstackLimitAddress(kind);#endif}uintptr_tJSContext::stackLimitForJitCode(JS::StackKindkind){#ifdef JS_SIMULATORreturnsimulator()->stackLimit();#elsereturnstackLimit(kind);#endif}voidJSContext::resetJitStackLimit(){// Note that, for now, we use the untrusted limit for ion. This is fine,// because it's the most conservative limit, and if we hit it, we'll bail// out of ion into the interpreter, which will do a proper recursion check.#ifdef JS_SIMULATORjitStackLimit=jit::Simulator::StackLimit();#elsejitStackLimit=nativeStackLimit[JS::StackForUntrustedScript];#endifjitStackLimitNoInterrupt=jitStackLimit;}voidJSContext::initJitStackLimit(){resetJitStackLimit();}JSVersionJSContext::findVersion(){if(JSScript*script=currentScript(nullptr,ALLOW_CROSS_COMPARTMENT))returnscript->getVersion();if(compartment()&&compartment()->behaviors().version()!=JSVERSION_UNKNOWN)returncompartment()->behaviors().version();if(!CurrentThreadCanAccessRuntime(runtime()))returnJSVERSION_DEFAULT;returnruntime()->defaultVersion();}#ifdef DEBUGJS::AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext*cxArg):cx(cxArg->helperThread()?nullptr:cxArg){if(cx){MOZ_ASSERT(cx->requestDepth||JS::CurrentThreadIsHeapBusy());MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));cx->checkRequestDepth++;}}JS::AutoCheckRequestDepth::~AutoCheckRequestDepth(){if(cx){MOZ_ASSERT(cx->checkRequestDepth!=0);cx->checkRequestDepth--;}}#endif#ifdef JS_CRASH_DIAGNOSTICSvoidCompartmentChecker::check(InterpreterFrame*fp){if(fp)check(fp->environmentChain());}voidCompartmentChecker::check(AbstractFramePtrframe){if(frame)check(frame.environmentChain());}#endifvoidAutoEnterOOMUnsafeRegion::crash(constchar*reason){charmsgbuf[1024];SprintfLiteral(msgbuf,"[unhandlable oom] %s",reason);MOZ_ReportAssertionFailure(msgbuf,__FILE__,__LINE__);MOZ_CRASH();}AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallbackAutoEnterOOMUnsafeRegion::annotateOOMSizeCallback=nullptr;voidAutoEnterOOMUnsafeRegion::crash(size_tsize,constchar*reason){{JS::AutoSuppressGCAnalysissuppress;if(annotateOOMSizeCallback)annotateOOMSizeCallback(size);}crash(reason);}